import { Appender } from '../interface/Appender'
import { LogContext } from '../interface/LogContext'
import { LoggerConfig, LoggerConfigItem, LoggerRootConfig } from '../interface/LoggerConfig'
import { LoggerLevel } from '../interface/LoggerLevel'

/**
 * LogContextImpl
 *
 * @author 冰凝
 * @date 2022/12/17 14:56
 */
export class LogContextImpl implements LogContext {
    public config: LoggerConfig
    private readonly configCache = new Map<string, LoggerConfigItem | null>()

    public constructor(config: LoggerConfig) {
        this.config = config
    }

    public isEnable(name: string, level: LoggerLevel): boolean {
        return level >= (this.getConfig(name).level ?? this.config.root.level)
    }

    public log(name: string, level: LoggerLevel, ...args: Array<any>): void {
        if (this.isEnable(name, level)) {
            for (const appender of this.getAppender(name)) {
                appender.append(name, level, ...args)
            }
        }
    }

    private getAppender(name: string): Array<Appender> {
        const getKey = (): string | Array<string> => {
            const config = this.getConfig(name)
            if (config['name'] && config.appender) {
                return config.appender
            }
            return this.config.root.appender
        }

        const key = getKey()
        if (typeof key === 'string') {
            return [ this.config.appender[key]! ]
        }

        return key.map(k => this.config.appender[k]!)
    }

    private getConfig(name: string): LoggerConfigItem | LoggerRootConfig {
        const { root, config = [] } = this.config

        // 优先使用缓存
        const cache = this.configCache.get(name)
        if (cache) {
            return cache
        }

        const startList = config.filter(config => name.startsWith(config.name))
        // 如果没有额外配置, 使用 root 配置
        if (startList.length <= 0) {
            this.configCache.set(name, null)
            return root
        }

        // 多个合适的配置项, 取最后一个
        const endConfig = startList[startList.length - 1]!
        this.configCache.set(name, endConfig)

        return endConfig
    }

}
